//
// Copyright (c) 2013-2022 The SRS Authors
//
// SPDX-License-Identifier: MIT or MulanPSL-2.0
//

#ifndef SRS_PROTOCOL_SRT_HPP
#define SRS_PROTOCOL_SRT_HPP

#include <srs_core.hpp>
#include <srs_protocol_st.hpp>

#include <map>
#include <vector>

class SrsSrtSocket;

// 初始化 srt的log日志
extern srs_error_t srs_srt_log_initialie();

// 套接字的无效值 -1
typedef int srs_srt_t;
extern srs_srt_t srs_srt_socket_invalid();

// 创建或者关闭套接字
// Create srt socket only, with libsrt's default option.
extern srs_error_t srs_srt_socket(srs_srt_t* pfd);
extern srs_error_t srs_srt_close(srs_srt_t fd);

// 状态套接字，默认选项都关闭
// Create srt socket with srs recommend default option(tsbpdmode=false,tlpktdrop=false,latency=0,sndsyn=0,rcvsyn=0)
extern srs_error_t srs_srt_socket_with_default_option(srs_srt_t* pfd);

// 服务器监听 fd 网卡地址 端口号
// For server, listen at SRT endpoint.
extern srs_error_t srs_srt_listen(srs_srt_t srt_fd, std::string ip, int port);

// 非阻塞读写
// Set read/write no block.
extern srs_error_t srs_srt_nonblock(srs_srt_t srt_fd);

// 设置 协程socket 的选项
// Set SRT options.

/**
 maxbw
单位: bytes/s, 默认值-1；

最大的发送带宽.
-1: 表示最大带宽为1Gbps
0: 由SRTO_INPUTBW的计算决定(不推荐在直播模式下设置为0)
>0: 带宽为bytes/s

 */
extern srs_error_t srs_srt_set_maxbw(srs_srt_t srt_fd, int maxbw);

/**
mss
单位: byte
单个发送报文最大的size。默认1500。
这个报文的大小指的是ip报文，其包含udp和srt协议报文的。

 */
extern srs_error_t srs_srt_set_mss(srs_srt_t srt_fd, int mss);

// 1316=188x7
//因为srt承载的媒体数据是mpegts封装，而每个mpegts的最小包是188bytes，所以payloadsize是188的倍数，默认是1316bytes(188x7)
extern srs_error_t srs_srt_set_payload_size(srs_srt_t srt_fd, int payload_size);

// 超时时间
extern srs_error_t srs_srt_set_connect_timeout(srs_srt_t srt_fd, int timeout);

// 设置用户连接的超时时间
extern srs_error_t srs_srt_set_peer_idle_timeout(srs_srt_t srt_fd, int timeout);

//按时间戳投递包模式(Timestamp based packet delivery)
//给每一个报文打上时间戳，应用层读取时，会按照报文时间戳的间隔读取
extern srs_error_t srs_srt_set_tsbpdmode(srs_srt_t srt_fd, bool tsbpdmode);

// 发送缓冲大小
extern srs_error_t srs_srt_set_sndbuf(srs_srt_t srt_fd, int sndbuf);
// 接收缓冲大小
extern srs_error_t srs_srt_set_rcvbuf(srs_srt_t srt_fd, int rcvbuf);

// 太晚而丢弃(Too-late Packet Drop)
//因为srt是针对于音视频的传输协议，接收方是基于报文时间戳或编码bitrate来上送报文给上层应用的。
//也就是说，如果报文在接收方latency timeout后到达，报文也会应为太晚到达而被丢弃。
//在直播模式下，tlpkdrop默认是true，因为直播对延时要求高
extern srs_error_t srs_srt_set_tlpktdrop(srs_srt_t srt_fd, bool tlpktdrop);

// 这个latency配置同时配置了recvlatency和peerlatency成同一个值。
//
//如果recvlatency配置，将使用recvlatency的配置；
//如果peerlatency配置，将使用peerlatency的配置；
extern srs_error_t srs_srt_set_latency(srs_srt_t srt_fd, int latency);

// 这是接收方缓存时间长度，其包括报文从发送方出发，通过网络，接收方接收，直到上送给上层媒体应用。
// 也就是说这个缓存时间长度，其应该大于RTT，并且要为多次丢包重传做好准备。

//推荐配置：
//  低延时网络：
//      如果应用对延时要求低，可以考虑配置的参数低于250ms(常人对音视频低于250ms的延时不会被影响)
//  长距离，RTT比较大：
//      因为传输距离长，RTT比较大，就不能配置太小的latency；
//      或者是重要的直播，不要求低延时，但是要求无卡顿播放，无抖动；
//      建议配置的latency >= 3*RTT, 因为其中包含丢包重传和ack/nack的周期
extern srs_error_t srs_srt_set_rcv_latency(srs_srt_t srt_fd, int rcv_latency);

//是srt发送方设置peerlatency，告知接收方的latency buffer的时间长度应该是多少；
// 如果接收方也配置了recvlatency，接收方就取两者中最大值作为latency buffer时间长度。

// 推荐配置：
//  低延时网络：
//      如果应用对延时要求低，可以考虑配置的参数低于250ms(常人对音视频低于250ms的延时不会被影响)
//  长距离，RTT比较大：
//      因为传输距离长，RTT比较大，就不能配置太小的latency；
//      或者是重要的直播，不要求低延时，但是要求无卡顿播放，无抖动；
//      建议配置的latency >= 3*RTT, 因为其中包含丢包重传和ack/nack的周期
extern srs_error_t srs_srt_set_peer_latency(srs_srt_t srt_fd, int peer_latency);

// 流id
extern srs_error_t srs_srt_set_streamid(srs_srt_t srt_fd, const std::string& streamid);

// 获取 协程socket 的选项
// Get SRT options.
extern srs_error_t srs_srt_get_maxbw(srs_srt_t srt_fd, int& maxbw);
extern srs_error_t srs_srt_get_mss(srs_srt_t srt_fd, int& mss);
extern srs_error_t srs_srt_get_payload_size(srs_srt_t srt_fd, int& payload_size);
extern srs_error_t srs_srt_get_connect_timeout(srs_srt_t srt_fd, int& timeout);
extern srs_error_t srs_srt_get_peer_idle_timeout(srs_srt_t srt_fd, int& timeout);
extern srs_error_t srs_srt_get_tsbpdmode(srs_srt_t srt_fd, bool& tsbpdmode);
extern srs_error_t srs_srt_get_sndbuf(srs_srt_t srt_fd, int& sndbuf);
extern srs_error_t srs_srt_get_rcvbuf(srs_srt_t srt_fd, int& rcvbuf);
extern srs_error_t srs_srt_get_tlpktdrop(srs_srt_t srt_fd, bool& tlpktdrop);
extern srs_error_t srs_srt_get_latency(srs_srt_t srt_fd, int& latency);
extern srs_error_t srs_srt_get_rcv_latency(srs_srt_t srt_fd, int& rcv_latency);
extern srs_error_t srs_srt_get_peer_latency(srs_srt_t srt_fd, int& peer_latency);
extern srs_error_t srs_srt_get_streamid(srs_srt_t srt_fd, std::string& streamid);

// Get SRT socket info.
extern srs_error_t srs_srt_get_local_ip_port(srs_srt_t srt_fd, std::string& ip, int& port);
extern srs_error_t srs_srt_get_remote_ip_port(srs_srt_t srt_fd, std::string& ip, int& port);

// 获取 srt 的状态
// Get SRT stats.
class SrsSrtStat
{
private:
    // 协程的数据接收监视器
    void* stat_;
public:
    SrsSrtStat();
    virtual ~SrsSrtStat();
public:
    int64_t pktRecv();
    int pktRcvLoss();
    int pktRcvRetrans();
    int pktRcvDrop();
public:
    int64_t pktSent();
    int pktSndLoss();
    int pktRetrans();
    int pktSndDrop();
public:
    srs_error_t fetch(srs_srt_t srt_fd, bool clear);
};

// Srt 轮询器，订阅取消订阅的事件并等待它们被触发
// Srt poller, subscribe/unsubscribed events and wait them fired.
class ISrsSrtPoller
{
public:
    ISrsSrtPoller();
    virtual ~ISrsSrtPoller();
public:
    virtual srs_error_t initialize() = 0;
    virtual srs_error_t add_socket(SrsSrtSocket* srt_skt) = 0;
    virtual srs_error_t mod_socket(SrsSrtSocket* srt_skt) = 0;
    virtual srs_error_t del_socket(SrsSrtSocket* srt_skt) = 0;
    // Wait for the fds in its epoll to be fired in specified timeout_ms, where the pn_fds is the number of active fds.
    // Note that for ST, please always use timeout_ms(0) and switch coroutine by yourself.
    virtual srs_error_t wait(int timeout_ms, int* pn_fds) = 0;
public:
    virtual int size() = 0;
};
// new SrsSrtPoller()
ISrsSrtPoller* srs_srt_poller_new();

// Srt ST socket, wrap SRT io and make it adapt to ST-thread.
class SrsSrtSocket
{
public:
    SrsSrtSocket(ISrsSrtPoller* srt_poller, srs_srt_t srt_fd);
    virtual ~SrsSrtSocket();
public: // IO API
    // 链接
    srs_error_t connect(const std::string& ip, int port);
    // 接收链接
    srs_error_t accept(srs_srt_t* client_srt_fd);
    // 接收消息
    srs_error_t recvmsg(void* buf, size_t size, ssize_t* nread);
    // 发送消息
    srs_error_t sendmsg(void* buf, size_t size, ssize_t* nwrite);
public:
    srs_srt_t fd() const { return srt_fd_; }
    int events() const { return events_; }
public:
    void set_recv_timeout(srs_utime_t tm) { recv_timeout_ = tm; }
    void set_send_timeout(srs_utime_t tm) { send_timeout_ = tm; }
    srs_utime_t get_send_timeout() { return send_timeout_; }
    srs_utime_t get_recv_timeout() { return recv_timeout_; }
    int64_t get_send_bytes() { return send_bytes_; }
    int64_t get_recv_bytes() { return recv_bytes_; }
    // 等待读 Yiled coroutine and wait this socket readable.
    srs_error_t wait_readable();
    // 等待写 Yiled coroutine and wait this socket writeable.
    srs_error_t wait_writeable();
    // 通知可读 Notify this socket readable, and resume coroutine later.
    void notify_readable();
    // 通知可写 Notify this socket writeable, and resume coroutine later.
    void notify_writeable();
    // 通知错误 Notify this socket error, resume coroutine later and error will return in all the operator of this socket.
    void notify_error();
public:
    // Subscribed IN/ERR event to srt poller.
    // 开启读
    srs_error_t enable_read();
    // 静止写
    // Unsubscribed IN event to srt poller.
    srs_error_t disable_read();
    // 开启写
    // Subscribed OUT/ERR event to srt poller.
    srs_error_t enable_write();
    // 禁止写
    // Unsubscribed OUT event to srt poller.
    srs_error_t disable_write();
private:
    // 设置事件
    srs_error_t enable_event(int event);
    // 禁用事件
    srs_error_t disable_event(int event);
    // 检查错误
    srs_error_t check_error();

private:
    // 协程套接字fd
    srs_srt_t srt_fd_;

    //当前是否有错误
    // Mark if some error occured in srt socket.
    bool has_error_;

    // When read operator like recvmsg/accept would block, wait this condition timeout or notified, 
    // and the coroutine itself will be yiled and resume when condition be notified.
    // 当像 recvmsg/accept 这样的读取运算符会阻塞时，等待此条件超时或通知，并且协程本身将在条件被通知时产生并恢复
    srs_cond_t read_cond_;

    // When write operator like sendmsg/connectt would block, wait this condition timeout or notified, 
    // and the coroutine itself will be yiled and resume when condition be notified.
    // 当像 sendmsgconnectt 这样的写操作符会阻塞时，等待这个条件超时或通知，协程本身将被 yiled 并在条件被通知时恢复。
    srs_cond_t write_cond_;

    // 接收、发送超时时间
    srs_utime_t recv_timeout_;
    srs_utime_t send_timeout_;

    // 接收、发送的数据大小
    int64_t recv_bytes_;
    int64_t send_bytes_;

    // Event of this socket subscribed.
    // 已订阅此套接字的事件
    int events_;
    // Srt poller which this socket attach to.
    // 套接字连接到的 Srt 轮询器
    ISrsSrtPoller* srt_poller_;
};

#endif

